using System;
using System.Collections.Specialized;
using System.Threading;
using System.Windows;
using System.Windows.Threading;

using DanielVaughan.Calcium.Content;
using DanielVaughan.Calcium.Gui;
using DanielVaughan.Gui;

using Microsoft.Practices.Composite.Presentation.Regions;
using Microsoft.Practices.Composite.Regions;

namespace DanielVaughan.Calcium.Modularity.RegionAdapters
{
	// Code from http://blogs.southworks.net/ibaumann/2008/09/26/windowregionadapter-for-compositewpf-prism/
	internal class WindowRegionAdapter : RegionAdapterBase<Window>
	{
		public WindowRegionAdapter(IRegionBehaviorFactory regionBehaviorFactory)
			: base(regionBehaviorFactory)
		{
			/* Intentionally left blank. */
		}

		protected override void Adapt(IRegion region, Window regionTarget)
		{
		}

		protected override IRegion CreateRegion()
		{
			return new SingleActiveRegion();
		}

		protected override void AttachBehaviors(IRegion region, Window regionTarget)
		{
			base.AttachBehaviors(region, regionTarget);

			var behavior = new WindowRegionBehavior(regionTarget, region, WindowStyle);
			behavior.Attach();
		}

		public Style WindowStyle { get; set; }

		class WindowRegionBehavior
		{
			readonly WeakReference ownerWeakReference;
			readonly WeakReference regionWeakReference;
			readonly Style windowStyle;

			internal WindowRegionBehavior(Window owner, IRegion region, Style windowStyle)
			{
				ownerWeakReference = new WeakReference(owner);
				regionWeakReference = new WeakReference(region);
				this.windowStyle = windowStyle;
			}

			internal void Attach()
			{
				var region = regionWeakReference.Target as IRegion;

				if (region != null)
				{
					region.Views.CollectionChanged += Views_CollectionChanged;
					region.ActiveViews.CollectionChanged += ActiveViews_CollectionChanged;
				}
			}

			internal void Detach()
			{
				var region = regionWeakReference.Target as IRegion;

				if (region != null)
				{
					region.Views.CollectionChanged -= Views_CollectionChanged;
					region.ActiveViews.CollectionChanged -= ActiveViews_CollectionChanged;
				}
			}

			void window_Activated(object sender, EventArgs e)
			{
				var region = regionWeakReference.Target as IRegion;
				var window = sender as Window;

				if (window != null && !region.ActiveViews.Contains(window.Content))
				{
					region.Activate(window.Content);
				}
			}

			void window_Deactivated(object sender, EventArgs e)
			{
				var region = regionWeakReference.Target as IRegion;
				var window = sender as Window;

				if (window != null)
				{
					region.Deactivate(window.Content);
				}
			}

			void window_Closed(object sender, EventArgs e)
			{
				var window = sender as Window;
				var region = regionWeakReference.Target as IRegion;

				if (window != null && region != null)
				{
					if (region.Views.Contains(window.Content))
					{
						ThreadPool.QueueUserWorkItem(delegate
                         	{
								var dispatcher = ServiceLocatorSingleton.Instance.GetInstance<Dispatcher>();
								dispatcher.InvokeIfRequired(delegate
									{
										try
										{
											var content = window.Content;
											window.Content = null;
											region.Remove(content);
										}
										catch (Exception)
										{
											/* Suppress. */
										}
									});
                         	});
					}
				}
			}

			void ActiveViews_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
			{
				var owner = ownerWeakReference.Target as Window;

				if (owner == null)
				{
					Detach();
					return;
				}

				if (e.Action == NotifyCollectionChangedAction.Add)
				{
					foreach (object view in e.NewItems)
					{
						Window window = GetContainerWindow(owner, view);

						if (window != null && !window.IsFocused)
						{
							window.WindowState = WindowState.Normal;
							window.Activate();
						}
					}
				}
			}

			void Views_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
			{
				var owner = ownerWeakReference.Target as Window;

				if (owner == null)
				{
					Detach();
					return;
				}

				if (e.Action == NotifyCollectionChangedAction.Add)
				{
					foreach (object view in e.NewItems)
					{
						var window = new Window();
						window.Activated += window_Activated;
						window.Deactivated += window_Deactivated;
						window.Style = windowStyle;
						window.Content = view;
						window.Closed += window_Closed;
						window.Owner = owner;
						window.WindowStartupLocation = owner != null 
							? WindowStartupLocation.CenterOwner : WindowStartupLocation.CenterScreen;

						bool modal = false;
						var optionsProvider = ViewModelBase.FindType<IProvider<IViewOptions>>(view);

						if (optionsProvider != null)
						{
							var item = optionsProvider.ProvidedItem;
							if (item == null)
							{
								throw new InvalidOperationException(
									"IProvider<IViewOptions>.ProvidedItem should not be null.");
							}

							modal = item.Modal;
							item.PrepareContainer(window);
						}

						var temp = view as IView;
						if (temp != null)
						{
							var viewModel = temp.ViewModel;
							if (viewModel != null)
							{
								window.DataContext = viewModel;
							}
						}

						if (window.DataContext == null)
						{
							window.DataContext = view;
						}

						if (modal)
						{
							window.ShowDialog();
						}
						else
						{
							window.Show();
						}
					}
				}
				else if (e.Action == NotifyCollectionChangedAction.Remove)
				{
					foreach (object view in e.OldItems)
					{
						Window window = GetContainerWindow(owner, view);

						if (window != null)
						{
							window.Close();
						}
					}
				}
			}

			Window GetContainerWindow(Window owner, object view)
			{
				foreach (Window window in owner.OwnedWindows)
				{
					if (window.Content == view)
					{
						return window;
					}
				}

				return null;
			}
		}
	}
}
